package mergecluster

import (
	"context"
	"time"

	"github.com/evanphx/json-patch"
	"github.com/pkg/errors"
	jsonpatchv2 "gomodules.xyz/jsonpatch/v2"
	confv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	bkenode "gopkg.openfuyao.cn/bkecommon/cluster/node"
	bkevalidate "gopkg.openfuyao.cn/bkecommon/cluster/validation"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/apimachinery/pkg/util/rand"
	"sigs.k8s.io/cluster-api/util/patch"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1beta1"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/statusmanage"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/annotation"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/condition"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/config"
	l "gopkg.openfuyao.cn/cluster-api-provider-bke/utils/log"
)

type bkeNodes struct {
	spec   bkenode.Nodes
	status confv1beta1.NodesStates
}

// SyncStatusUntilComplete sync bkecluster Status until complete
func SyncStatusUntilComplete(c client.Client, bkeCluster *v1beta1.BKECluster, patchs ...PatchFunc) (err error) {
	log := l.Named("syncer")
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()
	for {
		select {
		case <-ctx.Done():
			return errors.New("The update failed to complete after 2 minutes. ")
		default:
		}
		// Execute concurrent tasks at different peaks.
		// When the number of concurrent tasks is greater than 100, a random value of 1-15 is preferred
		//time.Sleep(time.Duration(rand.IntnRange(1, 5)) * time.Second)
		err = UpdateCombinedBKECluster(ctx, c, bkeCluster, []string{}, patchs...)
		if err != nil {
			if apierrors.IsNotFound(err) {
				log.Warnf("bkeCluster %q not found, skip update, error(ignore): %v", utils.ClientObjNS(bkeCluster), err)
				break
			}
			if apierrors.IsConflict(err) {
				log.Warnf("Update bkeCluster %q failed error: %v", utils.ClientObjNS(bkeCluster), err)
				continue
			}
			if apierrors.IsForbidden(err) || apierrors.IsBadRequest(err) || apierrors.IsInvalid(err) {
				return err
			}

			log.Warnf("Update bkeCluster %q failed error: %v", utils.ClientObjNS(bkeCluster), err)
			continue
		}
		time.Sleep(time.Duration(rand.IntnRange(0, 2)) * time.Second)
		break
	}
	return nil
}

func (b *bkeNodes) toCMData() (map[string]string, error) {
	data := map[string]string{}
	if b.spec != nil {
		specByte, err := json.Marshal(b.spec)
		if err != nil {
			return nil, err
		}
		data["nodes"] = string(specByte)
	}
	if b.status != nil {
		statusByte, err := json.Marshal(b.status)
		if err != nil {
			return nil, err
		}
		data["status"] = string(statusByte)
	}
	return data, nil
}

func GetCombinedBKECluster(ctx context.Context, c client.Client, namespace, name string) (*v1beta1.BKECluster, error) {
	bkeCluster := &v1beta1.BKECluster{}
	if err := c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, bkeCluster); err != nil {
		return nil, err
	}
	cm, err := GetCombinedBKEClusterCM(ctx, c, bkeCluster)
	if err != nil {
		return nil, err
	}

	return CombinedBKECluster(bkeCluster, cm)
}

type PatchFunc func(currentCombinedBkeCluster *v1beta1.BKECluster)

// UpdateCombinedBKECluster Update bkecluster and configmap
// ps:该函数很重要，每当有修改时，跑一遍单元测试，看看是否有问题
func UpdateCombinedBKECluster(ctx context.Context, c client.Client, combinedCluster *v1beta1.BKECluster, deleteNodes []string, patchs ...PatchFunc) error {
	currentCombinedBkeCluster, err := GetCombinedBKECluster(ctx, c, combinedCluster.Namespace, combinedCluster.Name)
	if err != nil {
		return err
	}

	currentCombinedBkeCluster.Status.PhaseStatus =
		fixPhaseStatus(currentCombinedBkeCluster.Status.PhaseStatus)

	for _, p := range patchs {
		p(currentCombinedBkeCluster)
		p(combinedCluster)
	}

	//查看当前currentCombinedBkeCluster是否有人为更新
	//
	//获取外部更新的内容
	patches, err := getCurrentBkeClusterPatches(combinedCluster.DeepCopy(), currentCombinedBkeCluster.DeepCopy())
	if err != nil {
		return err
	}

	// 如果有外部更新，将外部更新的内容合并到combinedCluster中
	if patches != nil {
		combinedByte, err := json.Marshal(combinedCluster)
		if err != nil {
			return err
		}
		apply, err := patches.Apply(combinedByte)
		if err != nil {
			return err
		}
		err = json.Unmarshal(apply, combinedCluster)
		if err != nil {
			return err
		}
	}

	currentBkeCluster := &v1beta1.BKECluster{}
	if err := c.Get(ctx, types.NamespacedName{Namespace: combinedCluster.Namespace, Name: combinedCluster.Name}, currentBkeCluster); err != nil {
		if apierrors.IsNotFound(err) {
			return nil
		}
		return err
	}
	// Initialize the patch helper
	patchHelper, err := patch.NewHelper(currentBkeCluster, c)
	if err != nil {
		return err
	}

	if config.EnableInternalUpdate {
		if len(patchs) != 0 {
			// 添加一个特殊的注解以防止内部的更新触发入队
			condition.ConditionMark(currentBkeCluster, v1beta1.InternalSpecChangeCondition, "", "", "")
			if err := patchHelper.Patch(ctx, currentBkeCluster); err != nil {
				return err
			}
		} else {
			if _, ok := condition.HasCondition(v1beta1.InternalSpecChangeCondition, currentBkeCluster); ok {
				condition.RemoveCondition(v1beta1.InternalSpecChangeCondition, currentBkeCluster)
				if err := patchHelper.Patch(ctx, currentBkeCluster); err != nil {
					return err
				}
			}
		}
	}

	// step 2 取出cm中的nodes和status
	cm, nodesCM, err := getBkeClusterAssociateNodesCM(ctx, c, combinedCluster)
	if err != nil {
		return err
	}

	// step 1 先构建一个新的bkecluster，除 spec.nodes he status.nodes其他字段沿用combinedCluster
	newBKECuster := newTmpBkeCluster(combinedCluster, currentBkeCluster)

	// step 3 从combinedCluster中取出spec.nodes和status.nodes
	// nodesCombined是经过调谐后的最终的spec.nodes和status.nodes
	// 创建时由combinedCluster中spec的nodes和status中的nodes组成
	nodesCombined := newTmpBkeNodesCluster(combinedCluster)

	// step 4 从currentBkeCluster中取出spec.nodes和status.nodes,并非最终的spec.nodes和status.nodes
	// nodesCluster是将要更新到newBKECuster中sepc.nodes和status.nodes的临时变量，
	// 创建时由currentBkeCluster中spec的nodes和status中的nodes组成
	finalClusterNodes := newTmpBkeNodesCluster(&v1beta1.BKECluster{})
	finalCMNodes := newTmpBkeNodesCluster(&v1beta1.BKECluster{})

	// step 5 从nodesCombined.spec.nodes中移除nodesCM中的spec.nodes,
	//        并将剩余的nodes添加到nodesCluster.spec.nodes中
	for _, node := range nodesCombined.spec {
		if utils.ContainsString(deleteNodes, node.IP) {
			continue
		}
		if nodesCM.spec.Filter(bkenode.FilterOptions{"IP": node.IP}).Length() == 1 {
			finalCMNodes.spec = append(finalCMNodes.spec, node)
			continue
		}

		finalClusterNodes.spec = append(finalClusterNodes.spec, node)
	}
	// step 6 从nodesCombined.status.nodes中移除nodesCM中的status.nodes,
	//        并将剩余的nodes添加到nodesCluster.status.nodes中
	for _, node := range nodesCombined.status {
		if nodesCM.status.GetNodeState(node.Node.IP) != "" {
			finalCMNodes.status = append(finalCMNodes.status, node)
			continue
		}
		if nodesCM.spec.Filter(bkenode.FilterOptions{"IP": node.Node.IP}).Length() == 1 {
			finalCMNodes.status = append(finalCMNodes.status, node)
			continue
		}

		finalClusterNodes.status = append(finalClusterNodes.status, node)
	}

	// step 7 将nodesCluster中的spec.nodes和status.nodes添加到newBKECuster中
	newBKECuster.Spec.ClusterConfig.Nodes = finalClusterNodes.spec
	newBKECuster.Status.NodesStatus = finalClusterNodes.status

	// fix phaseStatus too large > 1.5M
	newBKECuster.Status.PhaseStatus = fixPhaseStatus(newBKECuster.Status.PhaseStatus)

	// 将这次更新设置到newBKECuster的annotation中
	configByte, err := json.Marshal(cleanBkeCluster(newBKECuster))
	if err != nil {
		return err
	}
	annotation.SetAnnotation(newBKECuster, annotation.LastUpdateConfigurationAnnotationKey, string(configByte))

	statusmanage.BKEClusterStatusManager.SetStatus(newBKECuster)
	combinedCluster.Status.ClusterHealthState = newBKECuster.Status.ClusterHealthState
	combinedCluster.Status.ClusterStatus = newBKECuster.Status.ClusterStatus
	combinedCluster.Status.NodesStatus = newBKECuster.Status.NodesStatus
	combinedCluster.Status.Conditions = newBKECuster.Status.Conditions

	if config.EnableInternalUpdate {
		defer func() {
			if len(patchs) != 0 {
				condition.RemoveCondition(v1beta1.InternalSpecChangeCondition, newBKECuster)
				if err = patchHelper.Patch(ctx, newBKECuster); err != nil {
					return
				}
			}
		}()
	}

	// step 8 更新newBKECuster
	if err = patchHelper.Patch(ctx, newBKECuster); err != nil {
		return err
	}

	// step 9 更新cm
	data, err := finalCMNodes.toCMData()
	if err != nil {
		return err
	}
	cm.Data = data
	// 将这次更新设置到cm的annotation中
	cmByte, err := json.Marshal(cm)
	if err != nil {
		return err
	}
	annotation.SetAnnotation(cm, annotation.LastUpdateConfigurationAnnotationKey, string(cmByte))

	if err = c.Update(ctx, cm, &client.UpdateOptions{}); err != nil {
		if apierrors.IsNotFound(err) {
			return nil
		}
		return err
	}

	return nil
}

func DeleteCombinedBKECluster(ctx context.Context, c client.Client, namespace, name string) error {
	err := c.Delete(ctx, &v1beta1.BKECluster{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
		},
	}, &client.DeleteOptions{})
	if err != nil {
		return err
	}

	err = c.Delete(ctx, &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      name,
		}}, &client.DeleteOptions{})
	if err != nil {
		return err
	}
	return nil
}

func ListCombinedBKECluster(ctx context.Context, c client.Client) (*v1beta1.BKEClusterList, error) {
	clusterList := &v1beta1.BKEClusterList{}
	if err := c.List(ctx, clusterList); err != nil {
		return nil, err
	}

	newClusterList := &v1beta1.BKEClusterList{}
	for _, cl := range clusterList.Items {
		cm, err := GetCombinedBKEClusterCM(ctx, c, &cl)
		if err != nil {
			return nil, err
		}
		cl, err := CombinedBKECluster(&cl, cm)
		if err != nil {
			return nil, err
		}
		newClusterList.Items = append(newClusterList.Items, *cl)
	}
	return newClusterList, nil
}

func CombinedBKECluster(bkeCluster *v1beta1.BKECluster, cm *corev1.ConfigMap) (*v1beta1.BKECluster, error) {
	lastBkeClusterConfig, lastBkeFlag := annotation.HasAnnotation(bkeCluster, annotation.LastUpdateConfigurationAnnotationKey)
	lastCMConfig, lastCMFlag := annotation.HasAnnotation(cm, annotation.LastUpdateConfigurationAnnotationKey)

	if lastBkeFlag && lastCMFlag {
		annotation.RemoveAnnotation(bkeCluster, annotation.LastUpdateConfigurationAnnotationKey)
		lastBkeCluster := &v1beta1.BKECluster{}
		lastCM := &corev1.ConfigMap{}

		if err := json.Unmarshal([]byte(lastBkeClusterConfig), lastBkeCluster); err != nil {
			return nil, err
		}
		if err := json.Unmarshal([]byte(lastCMConfig), lastCM); err != nil {
			return nil, err
		}
		lastAnnotation, err := CombinedBKECluster(lastBkeCluster, lastCM)
		if err != nil {
			return nil, err
		}

		lastConfigByte, err := json.Marshal(lastAnnotation)
		if err != nil {
			return nil, err
		}
		annotation.SetAnnotation(bkeCluster, annotation.LastUpdateConfigurationAnnotationKey, string(lastConfigByte))
	}

	if bkeCluster.Spec.ClusterConfig.Nodes == nil {
		bkeCluster.Spec.ClusterConfig.Nodes = []confv1beta1.Node{}
	}

	if bkeCluster.Status.NodesStatus == nil {
		bkeCluster.Status.NodesStatus = confv1beta1.NodesStates{}
	}

	if v, ok := cm.Data["nodes"]; ok && v != "" {
		nodes := []confv1beta1.Node{}
		err := json.Unmarshal([]byte(v), &nodes)
		if err != nil {
			return nil, err
		}
		for _, n := range nodes {
			if err := bkevalidate.ValidateSingleNode(bkenode.Node(n)); err != nil {
				return nil, errors.Errorf("configMap node %v is invalid, err: %v", n, err)
			}
			bkeCluster.Spec.ClusterConfig.Nodes = append(bkeCluster.Spec.ClusterConfig.Nodes, n)
		}
	}

	if v, ok := cm.Data["status"]; ok && v != "" {
		nodes := confv1beta1.NodesStates{}
		err := json.Unmarshal([]byte(v), &nodes)
		if err != nil {
			return nil, err
		}
		for _, n := range nodes {
			bkeCluster.Status.NodesStatus = append(bkeCluster.Status.NodesStatus, n)
		}
	}

	if bkeCluster.Spec.ClusterConfig.CustomExtra == nil {
		bkeCluster.Spec.ClusterConfig.CustomExtra = map[string]string{}
	}

	return bkeCluster, nil
}

func GetCombinedBKEClusterCM(ctx context.Context, c client.Client, bkeCluster *v1beta1.BKECluster) (*corev1.ConfigMap, error) {
	cm := &corev1.ConfigMap{}
	namespace := bkeCluster.GetNamespace()
	name := bkeCluster.GetName()

	if err := c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, cm); err != nil {
		if apierrors.IsNotFound(err) {
			newCm := &corev1.ConfigMap{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: namespace,
					Name:      name,
				},
				Data: map[string]string{
					"nodes":  "[]",
					"status": "[]",
				},
			}
			// set owner reference
			if err := controllerutil.SetControllerReference(bkeCluster, newCm, c.Scheme()); err != nil {
				return nil, err
			}
			if err = c.Create(ctx, newCm, &client.CreateOptions{}); err != nil {
				if apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
					return newBlankBKEConfigMap(bkeCluster), nil
				}
				return nil, err
			}
			return newCm, nil
		}
		return nil, err
	}

	if cm.Data == nil {
		cm.Data = map[string]string{
			"nodes":  "[]",
			"status": "[]",
		}
	}

	return cm, nil
}

func getBkeClusterAssociateNodesCM(ctx context.Context, c client.Client, bkeCluster *v1beta1.BKECluster) (*corev1.ConfigMap, *bkeNodes, error) {
	cm, err := GetCombinedBKEClusterCM(ctx, c, bkeCluster)
	if err != nil {
		return nil, nil, err
	}
	nodesCM := &bkeNodes{}
	if v, ok := cm.Data["nodes"]; ok {
		err := json.Unmarshal([]byte(v), &nodesCM.spec)
		if err != nil {
			return nil, nil, err
		}
	}
	if v, ok := cm.Data["status"]; ok {
		err := json.Unmarshal([]byte(v), &nodesCM.status)
		if err != nil {
			return nil, nil, err
		}
	}
	annotation.RemoveAnnotation(cm, annotation.LastUpdateConfigurationAnnotationKey)
	return cm, nodesCM, nil
}

func newTmpBkeCluster(combinedCluster *v1beta1.BKECluster, currentBkeCluster *v1beta1.BKECluster) *v1beta1.BKECluster {
	// 关于这段赋值的说明
	// newBKECuster 是最终要更新到集群内的bkecluster
	// cluster 是组合的bkecluster
	// bkeCluster 是集群中还未更新的bkecluster
	// 在这个方法中，不止要将node信息分别更新到cm和bkecluster中，还要将其他的字段更新到bkecluster中
	// 首先直接拷贝cluster，让除node(spec、status)字段之外的字段都是最新的
	newBKECuster := combinedCluster.DeepCopy()
	// 保留现有集群中bkeCluster的metadata里的UID、ResourceVersion、Generation、SelfLink、CreationTimestamp、
	// DeletionTimestamp、DeletionGracePeriodSeconds等字段。
	// 如果不使用集群中的bkeCluster的metadata，会导致更新失败
	// 错误信息: Operation cannot be fulfilled on bkeclusters.bke.bocloud.io "bkecluster": the object has been modified; please apply your changes to the latest version and try again
	newBKECuster.ObjectMeta = *currentBkeCluster.ObjectMeta.DeepCopy()
	// 使用最新的Labels、Annotations、OwnerReferences、Finalizers
	newBKECuster.Annotations = combinedCluster.Annotations
	annotation.RemoveAnnotation(newBKECuster, annotation.LastUpdateConfigurationAnnotationKey)
	newBKECuster.Labels = combinedCluster.Labels
	newBKECuster.OwnerReferences = combinedCluster.OwnerReferences
	newBKECuster.Finalizers = combinedCluster.Finalizers
	// status的Nodes清空，将在后面重新计算赋值
	newBKECuster.Spec.ClusterConfig.Nodes = []confv1beta1.Node{}
	// status的Nodes清空，将在后面重新计算赋值
	newBKECuster.Status.NodesStatus = confv1beta1.NodesStates{}
	return newBKECuster
}

// newTmpBkeNodesCluster
// 创建时由bkeCluster和nodesCM中的nodes和status中的nodes组成
func newTmpBkeNodesCluster(bkeCluster *v1beta1.BKECluster) *bkeNodes {
	nodesCluster := &bkeNodes{}
	if bkeCluster.Spec.ClusterConfig == nil || bkeCluster.Spec.ClusterConfig.Nodes == nil {
		nodesCluster.spec = []confv1beta1.Node{}
	} else {
		nodesCluster.spec = bkeCluster.Spec.ClusterConfig.Nodes
	}

	if bkeCluster.Status.NodesStatus == nil {
		nodesCluster.status = confv1beta1.NodesStates{}
	} else {
		nodesCluster.status = bkeCluster.Status.NodesStatus
	}

	return nodesCluster
}

// getCurrentBkeClusterPatches 忽略status
func getCurrentBkeClusterPatches(old, new *v1beta1.BKECluster) (jsonpatch.Patch, error) {
	//去除last-update注释
	annotation.RemoveAnnotation(old, annotation.LastUpdateConfigurationAnnotationKey)
	annotation.RemoveAnnotation(new, annotation.LastUpdateConfigurationAnnotationKey)

	// 只考虑spec，labels，annotations,finalizers
	// 清空其余字段
	old.ObjectMeta = metav1.ObjectMeta{
		Labels:          old.ObjectMeta.Labels,
		Annotations:     old.ObjectMeta.Annotations,
		ResourceVersion: old.ObjectMeta.ResourceVersion,
	}
	new.ObjectMeta = metav1.ObjectMeta{
		Labels:          new.ObjectMeta.Labels,
		Annotations:     new.ObjectMeta.Annotations,
		ResourceVersion: new.ObjectMeta.ResourceVersion,
	}
	// 忽略status
	old.Status = confv1beta1.BKEClusterStatus{}
	new.Status = confv1beta1.BKEClusterStatus{}

	oldBytes, err := json.Marshal(old)
	if err != nil {
		return nil, err
	}
	newBytes, err := json.Marshal(new)
	if err != nil {
		return nil, err
	}

	patches, err := jsonpatchv2.CreatePatch(oldBytes, newBytes)
	if err != nil {
		return nil, err
	}

	if patches == nil || len(patches) == 0 {
		return nil, nil
	}

	pBytes, err := json.Marshal(patches)
	if err != nil {
		return nil, err
	}

	decodePatch, err := jsonpatch.DecodePatch(pBytes)
	if err != nil {
		return nil, err
	}
	return decodePatch, nil
}

func cleanBkeCluster(bkeCluster *v1beta1.BKECluster) *v1beta1.BKECluster {
	cluster := &v1beta1.BKECluster{}
	cluster.Name = bkeCluster.Name
	cluster.Namespace = bkeCluster.Namespace
	cluster.Spec = *bkeCluster.Spec.DeepCopy()
	return cluster
}

func GetLastUpdatedBKECluster(bkeCluster *v1beta1.BKECluster) (*v1beta1.BKECluster, error) {
	oldBkeCluster := &v1beta1.BKECluster{}
	v, ok := annotation.HasAnnotation(bkeCluster, annotation.LastUpdateConfigurationAnnotationKey)
	if !ok || v == "" {
		return nil, nil
	} else {
		err := json.Unmarshal([]byte(v), oldBkeCluster)
		if err != nil {
			return nil, err
		}
	}
	return oldBkeCluster, nil
}

func newBlankBKEConfigMap(bkeCluster *v1beta1.BKECluster) *corev1.ConfigMap {
	return &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      bkeCluster.Name,
			Namespace: bkeCluster.Namespace,
		},
		Data: map[string]string{
			"nodes":  "[]",
			"status": "[]",
		},
	}
}

func fixPhaseStatus(ph confv1beta1.PhaseStatus) confv1beta1.PhaseStatus {
	if len(ph) == 0 {
		return ph
	}

	const maxEnsureClusterFailures = 3

	deduped := deduplicatePhaseStatus(ph)

	lastEnsureIndices := getLastFailedEnsureClusterIndices(deduped, maxEnsureClusterFailures)
	if len(lastEnsureIndices) == 0 {
		return deduped
	}

	return removeOldFailedEnsureCluster(deduped, lastEnsureIndices)
}

func deduplicatePhaseStatus(ph confv1beta1.PhaseStatus) confv1beta1.PhaseStatus {
	type key struct {
		Name    confv1beta1.BKEClusterPhase
		Status  confv1beta1.BKEClusterPhaseStatus
		Message string
	}

	lastIndex := make(map[key]int)
	for index, value := range ph {
		mapKey := key{
			Name:    value.Name,
			Status:  value.Status,
			Message: value.Message,
		}
		lastIndex[mapKey] = index
	}

	var deduped confv1beta1.PhaseStatus
	for index, value := range ph {
		mapKey := key{
			Name:    value.Name,
			Status:  value.Status,
			Message: value.Message,
		}
		if lastIndex[mapKey] == index {
			deduped = append(deduped, value)
		}
	}
	return deduped
}

func getLastFailedEnsureClusterIndices(ph confv1beta1.PhaseStatus, maxFailures int) []int {
	var indices []int
	for i := len(ph) - 1; i >= 0; i-- {
		if ph[i].Name == "EnsureCluster" && ph[i].Status == v1beta1.PhaseFailed {
			indices = append(indices, i)
			if len(indices) == maxFailures {
				break
			}
		}
	}
	return indices
}

func removeOldFailedEnsureCluster(ph confv1beta1.PhaseStatus, indices []int) confv1beta1.PhaseStatus {
	lastStart := indices[len(indices)-1]
	tail := ph[lastStart:]

	var cleaned confv1beta1.PhaseStatus
	for i := 0; i < lastStart && i < len(ph); i++ {
		if ph[i].Name == "EnsureCluster" && ph[i].Status == v1beta1.PhaseFailed {
			continue
		}
		cleaned = append(cleaned, ph[i])
	}

	return append(cleaned, tail...)
}
